/*
 * Copyright (c) 2021-2023 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */
#include <stdio.h>
#include <assert.h>
#include "board.h"
#include "hpm_sysctl_drv.h"
#include "hpm_can_drv.h"
#include "hpm_gptmr_drv.h"
#include <string.h>
typedef struct {
    CAN_Type *can_base;
    uint32_t clock_freq;
    int32_t irq_index;
} can_info_t;

/**
 * @brief Decode the CAN messge size from DLC
 *
 * @param [in] dlc Data Length Code
 * @return decoded CAN message size in bytes
 */
static uint8_t can_get_data_bytes_from_dlc(uint32_t dlc);

/**
 * @brief CAN multiple CAN message transmission test
 */
void board_can_send_multiple_can_messages(void);


uint8_t can_code_100ms[8] = {0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc};
uint8_t can_code_1s[8] = {0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77};

void trans_can_data(uint8_t* data,uint32_t size,uint32_t transtime)
{
    can_transmit_buf_t tx_buf;
    memset(&tx_buf, 0, sizeof(tx_buf));
    tx_buf.dlc = 8;
    uint32_t msg_len = can_get_data_bytes_from_dlc(tx_buf.dlc);
    for (uint32_t i = 0; i < size; i++) {
        tx_buf.data[i] = data[i];
    }
    for (uint32_t i = 0; i < transtime; i++) {
        tx_buf.id = i;
        hpm_stat_t status = can_send_message_blocking(BOARD_APP_CAN_BASE, &tx_buf);
        if (status != status_success) {
            return;
        }
    }
}


void timer_config_ms(GPTMR_Type * GPTMR,uint8_t ch_index,uint32_t priority,uint16_t ms)
{
    uint32_t gptmr_freq;
    gptmr_channel_config_t config;
    uint32_t GPTMR_BASE_INDEX = (uint32_t)(GPTMR - HPM_GPTMR0_BASE);
    uint8_t GPTMR_Number = GPTMR_BASE_INDEX/0x4000;
    clock_name_t clock_name;
    switch(GPTMR_Number)
    {
        case 0:clock_name = clock_gptmr0; break;
        case 1:clock_name = clock_gptmr1; break;
        case 2:clock_name = clock_gptmr2; break;
        case 3:clock_name = clock_gptmr3; break;
        case 4:clock_name = clock_gptmr4; break;
        case 5:clock_name = clock_gptmr5; break;
        case 6:clock_name = clock_gptmr6; break;
        case 7:clock_name = clock_gptmr7; break;
    }

    gptmr_channel_get_default_config(GPTMR, &config);

    gptmr_freq = clock_get_frequency(clock_name);
    config.reload = gptmr_freq / 1000 * ms;
    gptmr_channel_config(GPTMR, ch_index, &config, false);
    gptmr_start_counter(GPTMR, ch_index);

    gptmr_enable_irq(GPTMR, GPTMR_CH_RLD_IRQ_MASK(ch_index));

    intc_m_enable_irq_with_priority((IRQn_GPTMR0+GPTMR_Number), priority);
}

void tick_ms_isr(void)
{
    static uint8_t irq_count = 0;
    if (gptmr_check_status(HPM_GPTMR3, GPTMR_CH_RLD_STAT_MASK(0))) {
        gptmr_clear_status(HPM_GPTMR3, GPTMR_CH_RLD_STAT_MASK(0));
        if(irq_count<9)
        {
            irq_count++;
            trans_can_data(can_code_100ms,8,20);
        }else{
            trans_can_data(can_code_100ms,8,20);
            trans_can_data(can_code_1s,8,1);
            irq_count = 0;
        }
    }
}
SDK_DECLARE_EXT_ISR_M(IRQn_GPTMR3, tick_ms_isr);

static can_info_t s_can_info[] = {
        { .can_base = HPM_CAN0, .irq_index = IRQn_CAN0 },
        { .can_base = HPM_CAN1, .irq_index = IRQn_CAN1 },
#if defined(HPM_CAN2)
        { .can_base = HPM_CAN2, .irq_index = IRQn_CAN2 },
#endif
#if defined (HPM_CAN3)
        { .can_base = HPM_CAN3, .irq_index = IRQn_CAN3 },
#endif
};


void can_send_init(void)
{
    CAN_Type *ptr = BOARD_APP_CAN_BASE;
    can_config_t can_config;
    can_get_default_config(&can_config);
    can_config.baudrate = 500000; /* 500kbps */
    can_config.mode = can_mode_normal;
    board_init_can(ptr);
    uint32_t can_src_clk_freq = board_init_can_clock(ptr);
    hpm_stat_t status = can_init(ptr, &can_config, can_src_clk_freq);
    if (status != status_success) {
        return;
    }
}


int main(void)
{
    /* Initialize CAN */
    for (uint32_t i=0; i < ARRAY_SIZE(s_can_info); i++) {
        can_info_t  *info = &s_can_info[i];
        board_init_can(info->can_base);
        info->clock_freq = board_init_can_clock(info->can_base);
    }

    timer_config_ms(HPM_GPTMR3,0,4,100);

    can_send_init();
    
    while(1);
    return 0;
}

static uint8_t can_get_data_bytes_from_dlc(uint32_t dlc)
{
    uint32_t data_bytes = 0;

    dlc &= 0xFU;
    if (dlc <= 8U) {
        data_bytes = dlc;
    }
    else {
        switch (dlc) {
        case can_payload_size_12:
            data_bytes = 12U;
            break;
        case can_payload_size_16:
            data_bytes = 16U;
            break;
        case can_payload_size_20:
            data_bytes = 20U;
            break;
        case can_payload_size_24:
            data_bytes = 24U;
            break;
        case can_payload_size_32:
            data_bytes = 32U;
            break;
        case can_payload_size_48:
            data_bytes = 48U;
            break;
        case can_payload_size_64:
            data_bytes = 64U;
            break;
        default:
            /* Code should never touch here */
            break;
        }
    }

    return data_bytes;
}
